<template>
	<div class="canvas-wrap">
		<!-- 使用canvas元素作为画布 -->
		<canvas ref="cv" width="720" height="360"></canvas>
	</div>
</template>

<script setup>
// 导入Vue的生命周期钩子和响应式API
import { onMounted, onBeforeUnmount, ref } from 'vue'

// 获取canvas元素的引用
const cv = ref(null)
// 存储canvas的2D上下文
let ctx
// 存储动画帧请求ID
let rafId

// 定义画布宽高和粒子数量
const W = 1920
const H = 360
const N = 40
// 创建粒子数组，每个粒子包含位置、速度和半径属性
const particles = Array.from({ length: N }, () => ({
	x: Math.random() * W, // 粒子x坐标
	y: Math.random() * H, // 粒子y坐标
	vx: (Math.random() - 0.5) * 1.5, // x轴速度
	vy: (Math.random() - 0.5) * 1.5, // y轴速度
	r: Math.random() * 2 + 1 // 粒子半径
}))

/**
 * 绘制函数
 * 负责清除画布并绘制所有粒子和它们之间的连线
 */
function draw() {
	// 清除整个画布
	ctx.clearRect(0, 0, W, H)
	// 连接近邻
	for (let i = 0; i < N; i++) {
		for (let j = i + 1; j < N; j++) {
			const dx = particles[i].x - particles[j].x
			const dy = particles[i].y - particles[j].y
			const d2 = dx * dx + dy * dy
			if (d2 < 120 * 120) {
				ctx.strokeStyle = 'rgba(106,90,249,0.15)'
				ctx.lineWidth = 1
				ctx.beginPath()
				ctx.moveTo(particles[i].x, particles[i].y)
				ctx.lineTo(particles[j].x, particles[j].y)
				ctx.stroke()
			}
		}
	}
	// 粒子
	for (const p of particles) {
		ctx.fillStyle = '#6a5af9'
		ctx.beginPath()
		ctx.arc(p.x, p.y, p.r, 0, Math.PI * 2)
		ctx.fill()
	}
}

function step() {
	for (const p of particles) {
		p.x += p.vx
		p.y += p.vy
		if (p.x < 0 || p.x > W) p.vx *= -1
		if (p.y < 0 || p.y > H) p.vy *= -1
	}
	draw()
	rafId = requestAnimationFrame(step)
}

onMounted(() => {
	ctx = cv.value.getContext('2d')//获取canvas的2D上下文
	step()//开始动画
})

onBeforeUnmount(() => {
	if (rafId) cancelAnimationFrame(rafId)
})
</script>

<style scoped>
.canvas-wrap {
	padding: 12px;
}
canvas {
	width: 100%;
	max-width: 100%;
	background: #0f1024;
	border-radius: 8px;
	box-shadow: 0 6px 18px rgba(0,0,0,0.25);
}
</style>